<script>
// import { createConversation, deleteConversation, getConversationList, getMessageList, updateConversationTitle } from '@/api/ai/ai'
// import { getToken } from '@/utils/auth'
import { fetchEventSource } from '@microsoft/fetch-event-source'
import { Base64 } from 'js-base64'
import Speech from 'speak-tts'
import MarkdownView from './MarkdownView.vue'

const controller = new AbortController()
export default {
  name: 'AiChat',
  components: {
    MarkdownView,
  },
  data() {
    return {
      visible: false,
      queryInfo: {
        title: '',
      },
      flag: true,
      tts: null,
      conversationList: [],
      inputChatMessage: {
        content: '',
        conversationId: null,
      },
      messageStream: '',
      messageList: [],
      receiving: false,
      listenStr: '', // 实时拼接的完整文本
      lastSpokenIndex: 0, // 已朗读的文本位置
      isSpeaking: false, // 是否正在朗读
      textQueue: [], // 待朗读的句子队列
      bufferTimer: null, // 防抖定时器
      setInterflag: null,
    }
  },
  watch: {
    'inputChatMessage.conversationId': {
      handler(newVal) {
        this.messageList = []
        this.$nextTick(() => {
          this.$refs.inputChatMessageRef.focus()
        })
        this.initMessageForm()
        this.messageStream = ''
        if (newVal) {
          // 获取当前会话的消息列表
          getMessageList(newVal).then((res) => {
            this.messageList = res.data || []
            console.log('获取消息列表', this.messageList)
            this.scrollToBottom()
          })
        }
      },
    },
  },
  mounted() {
    this.tts = new Speech()
    this.tts.init({
      volume: 1,
      rate: 1,
      pitch: 1,
      lang: 'zh-CN',
    },
    ).then((data) => {
      // console.log('Speech准备完成', data)
    }).catch((e) => {
      console.error('speech初始化失败: ', e)
    })
  },
  methods: {
    stopread() {
      this.tts.cancel()
      this.isSpeaking = false
    },
    open() {
      this.visible = true
      this.refreshConversationList()
    },
    close() {
      this.visible = false
    },
    async createNewSession() {
      this.inputChatMessage.conversationId = null
      const res = await get('/qwen/createConversation')
      this.inputChatMessage.conversationId = res.data
      this.refreshConversationList()
    },
    refreshConversationList() {
      getConversationList(this.queryInfo).then((res) => {
        console.log('获取会话列表', res)
        this.conversationList = res.data || []
      })
    },
    selectConversation(id) {
      if (!this.receiving) {
        this.inputChatMessage.conversationId = id
      }
    },
    // 按下Shift+Enter换行，按下Enter发送
    async handleKeydown(event) {
      if (event.key === 'Enter') {
        if (event.shiftKey) {
          // 插入换行
          this.inputChatMessage.content += '\r\n'
          event.preventDefault()
        }
        else {
          await this.sendMessage()
          event.preventDefault()
        }
      }
    },
    uploadImage() {
      console.log('上传图片')
    },
    initMessageForm() {
      this.inputChatMessage.files = []
      this.inputChatMessage.content = ''
    },
    searchConversation() {
      this.refreshConversationList()
    },
    async sendMessage() {
      if (!this.inputChatMessage.content) {
        this.$message.error('请输入消息内容')
        return
      }

      this.messageStream = ''
      // 发送消息，消息参数：会话ID-conversationId，消息内容-content，消息类型-messageType，文件URL(外网可直接访问的URL)-fileUrls
      const bodyData = {
        // conversationId: this.inputChatMessage.conversationId,
        message: this.inputChatMessage.content,
        messageType: '1', // this.inputChatMessage.files.length > 0 ? '2' : '1',
        // imgUrls: this.inputChatMessage.files?.map(file => file.url) || [],
      }

      // 将当前回复的消息追加到 messageList
      this.messageList.push({
        roleType: 'USER',
        type: 1,
        content: this.inputChatMessage.content,
        // conversationId: this.inputChatMessage.conversationId,
        // imgUrls: this.inputChatMessage.files?.map(file => file.url) || [],
        createTime: new Date().toLocaleString(),
      })
      this.scrollToBottom()

      // 使用 fetch-event-source
      this.receiving = true

      fetchEventSource(`/api/chat/sendMessage`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(bodyData),
        signal: controller.signal,
        onopen: (response) => {
          console.log('Connection established', response)
          if (!response.ok) {
            this.$message.error(`消息发送失败:${response.statusText}`)
          }
        },
        onmessage: (event) => {
          const data = event.data
          console.log('decodedData', Base64.decode(data))
          this.inputChatMessage.content = ''
          this.inputChatMessage.files = []
          // 将流式数据 decode 后拼接到 messageStream 中
          this.messageStream = this.messageStream + Base64.decode(data)
          this.listenStr = this.listenStr + Base64.decode(data)
          this.scrollToBottom()
          // 处理新数据（防抖：300ms 内多次触发合并为一次）
          if (this.bufferTimer)
            clearTimeout(this.bufferTimer)
          this.bufferTimer = setTimeout(() => {
            this.processNewText()
          }, 20)
        },
        onclose: () => {
          this.receiving = false
          // 将当前消息追加到 messageList
          this.messageList.push({
            roleType: 'ASSISTANT',
            type: 2,
            content: this.messageStream,
            // conversationId: this.inputChatMessage.conversationId,
            createTime: new Date().toLocaleString(),
          })
          this.messageStream = ''
          //   // 刷新 messageList
          //   getMessageList(this.inputChatMessage.conversationId).then(res => {
          //     this.messageList = res
          //     this.scrollToBottom()
          //   })
          this.scrollToBottom()
        },
        onerror: (error) => {
          console.error('Error occurred:', error)
          this.receiving = false
        },
      })
    },
    abortRequest() {
      console.log('中断请求')
      controller.abort()
      this.receiving = false
    },
    // 处理新增文本并分句
    processNewText() {
      const unspokenText = this.listenStr.slice(this.lastSpokenIndex)
      if (!unspokenText.trim()) {
        this.isSpeaking = false
        return
      };

      // 分句逻辑（按标点分割）
      // const sentences = this.splitIntoSentences(unspokenText);
      // sentences.forEach(sentence => {
      this.textQueue = unspokenText
      // });

      // 更新已朗读位置
      this.lastSpokenIndex = this.listenStr.length

      // 若未在朗读，触发队列播放

      this.playNextFromQueue()
    },

    // 简单分句方法（按句号、感叹号、问号分割）
    // splitIntoSentences(text) {
    //   // 匹配以句号、感叹号、问号、分号或换行符结尾的句子
    //   const sentenceRegex = /[^。！？；\n]+[。！？；\n]*/g;
    //   return text.match(sentenceRegex) || [];
    // },

    // 从队列中播放下一句
    playNextFromQueue() {
      if (this.setInterflag) {
        clearInterval(this.setInterflag)
      }
      const sentence = this.textQueue.replace(/#/g, '')
      console.log(sentence, '111')
      this.isSpeaking = true
      this.tts.speak({ text: sentence })
      this.setInterflag = setInterval(() => {
        if (!this.tts.speaking()) {
          this.isSpeaking = false
        }
      }, 1000)
    },

    // 滚动 ai-chat-record 到最底部
    scrollToBottom() {
      this.$nextTick(() => {
        this.$refs.aiChatRecord.scrollTop = this.$refs.aiChatRecord.scrollHeight
      })
    },
    copyMessage(content) {
      console.log('复制消息', content)
      navigator.clipboard.writeText(content)
      this.$message.success('复制成功')
    },
    handleClose() {
      this.initMessageForm()
      this.messageList = []
      this.conversationList = []
      this.isSpeaking = false
    },
    editConversationTitle(id) {
      console.log('编辑会话标题', id)
      // 弹出对话框，输入标题，确认后更新会话标题
      this.$prompt('请输入会话标题', '编辑会话标题', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
      }).then(({ value }) => {
        console.log('会话标题', value)
        updateConversationTitle(id, value).then((res) => {
          this.$message.success('会话标题更新成功')
          this.refreshConversationList()
        })
      })
    },
    deleteConversation(id) {
      console.log('删除会话', id)
      // 询问是否删除
      this.$confirm('确定要删除该会话吗？', '删除会话', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
      }).then(() => {
        deleteConversation(id).then((res) => {
          this.$message.success('会话删除成功')
          this.refreshConversationList()
          this.inputChatMessage.conversationId = null
        })
      })
    },
  },
}
</script>

<template>
  <div class="ai-chat-container h-screen">
    <div class="ai-chat-left hidden h-full b px-3 pt-3">
      <el-button type="primary" size="small" icon="el-icon-plus" style="width: 100%" @click="createNewSession">创建新会话</el-button>
      <el-divider />
      <div class="ai-chat-list">
        <div class="ai-chat-list-search">
          <el-input v-model="queryInfo.title" size="small" clearable placeholder="请输入" />
          <el-button type="primary" size="small" icon="i-ep-search" @click="searchConversation">搜索</el-button>
        </div>
        <template v-if="conversationList.length === 0">
          <el-empty description="暂无会话" />
        </template>
        <template v-else>
          <div v-for="item in conversationList" :key="item.id" class="ai-chat-list-item" :class="{ 'ai-chat-list-item-active': item.id === inputChatMessage.conversationId }" @click="selectConversation(item.id)">
            <div class="ai-chat-list-item-left">
              <div class="ai-chat-list-item-title">
                <i class="el-icon-chat-line-square" :style="{ color: item.id === inputChatMessage.conversationId ? '#335ab7' : '#999' }" />
                <span style="margin-left: 5px">{{ item.title }}</span>
              </div>
              <div class="ai-chat-list-item-time">{{ item.createTime }}</div>
            </div>
            <div class="ai-chat-list-item-right">
              <el-button type="text" size="mini" icon="el-icon-edit" @click="editConversationTitle(item.id)" />
              <el-button type="text" size="mini" icon="el-icon-delete" @click="deleteConversation(item.id)" />
            </div>
          </div>
        </template>
      </div>
    </div>
    <!-- 右侧区域需要充满高度 -->
    <el-form ref="form" class="ai-chat-right h-full" :model="inputChatMessage" label-width="140px">
      <div ref="aiChatRecord" class="ai-chat-record">
        <!-- 展示聊天对话列表样式，左侧是AI，右侧是用户，根据 roleType 来判断，样式参考 chatgpt -->
        <div v-for="item in messageList" :key="item.id" class="ai-chat-record-item">
          <div v-if="item.roleType === 'USER'" class="ai-chat-record-item-user">
            <div>{{ item.content }}</div>
            <div v-if="item.imgUrls && item.imgUrls.length">
              <el-image v-for="imgUrl in typeof item.imgUrls === 'string' ? item.imgUrls.split(',') : item.imgUrls" :key="imgUrl" :src="imgUrl" style="width: 100px; height: 100px" fit="cover" :preview-src-list="typeof item.imgUrls === 'string' ? item.imgUrls.split(',') : item.imgUrls" :preview-teleported="true" />
            </div>
          </div>
          <div v-else class="ai-chat-record-item-assistant" style="position: relative">
            <MarkdownView :content="item.content" />
            <!-- 右上角添加复制按钮 -->
            <!-- <el-button
                type="text"
                size="mini"
                icon="el-icon-copy"
                style="position: absolute; top: 0; right: 7px; font-size: 12px; color: #999"
                @click="copyMessage(item.content)"
                >复制</el-button
              > -->
          </div>
        </div>
        <!-- 展示正在发送的消息 -->
        <div class="ai-chat-record-item">
          <div v-if="messageStream" class="ai-chat-record-item-assistant">
            <MarkdownView :content="messageStream" />
          </div>
        </div>
      </div>
      <div class="ai-chat-btns">
        <!-- <ty-upload class="ai-chat-upload" v-model="inputChatMessage.files" value-type="Object" list-type="picture" :limit="5" name="files" hide-upload-label accept=".png,.jpg" label-width="0" :disabled="!inputChatMessage.conversationId || receiving">
            <el-button plain size="mini" icon="el-icon-upload">上传图片</el-button>
          </ty-upload> -->
      </div>
      <div class="ai-chat-input">
        <textarea ref="inputChatMessageRef" v-model="inputChatMessage.content" class="ai-chat-input-textarea" placeholder="请输入" :disabled="receiving" style="width: 100%; height: 100px; resize: none" rows="5" :autosize="{ minRows: 5, maxRows: 10 }" />
        <!-- 发送按钮相对于输入框进行定位，在输入框的右下角 -->
        <el-button v-if="!receiving" type="primary" size="small" style="position: absolute; bottom: 5px; right: 20px" :loading="receiving" @click="sendMessage">发送</el-button>
        <i v-else class="el-icon-video-pause chat-abort-icon" @click="abortRequest" />
        <template>
          <!-- <el-button v-if="isSpeaking" type="primary" size="small" style="position: absolute; bottom: 5px; right: 100px" @click="stopread">停止</el-button> -->
        </template>
      </div>
      <div class="ai-chat-conversation-id">当前会话 ID: {{ inputChatMessage.conversationId }}</div>
    </el-form>

  </div>
</template>

<style lang="scss" scoped>
.ai-chat-container {
  display: flex;
  flex-direction: row;
  gap: 20px;
  overflow: auto;

  .ai-chat-left {
    width: 300px;
    overflow-y: auto;

    .ai-chat-list {
      overflow-y: auto;

      .ai-chat-list-search {
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        align-items: center;
        gap: 10px;
        margin-bottom: 10px;
      }

      .ai-chat-list-item {
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        align-items: center;
        gap: 10px;
        padding: 10px;
        border: 1px solid #e0e0e0;
        border-radius: 10px;
        margin-bottom: 10px;
        cursor: pointer;

        &:hover {
          background: linear-gradient(180deg, #f5f7fa -66%, #fff 66%, #fff);
          transition: all 0.1s linear;
          border: 1px solid #335ab7;
        }

        .ai-chat-list-item-left {
          flex: 1;
          display: flex;
          flex-direction: column;
          justify-content: flex-start;
          align-items: flex-start;

          .ai-chat-list-item-title {
            font-size: 14px;
            color: #333;
            margin-bottom: 10px;
          }

          .ai-chat-list-item-time {
            font-size: 12px;
            color: #999;
          }
        }

        .ai-chat-list-item-right {
          display: flex;
          flex-direction: row;
          justify-content: flex-end;
          align-items: center;
        }
      }

      .ai-chat-list-item-active {
        background: linear-gradient(180deg, #f5f7fa -66%, #fff 66%, #fff);
        transition: all 0.1s linear;
        border: 1px solid #335ab7;
      }
    }
  }

  .ai-chat-right {
    display: flex;
    flex-direction: column;
    flex: 1;
    background-color: #f9f9f9;
    border: 1px solid #e0e0e0;
    border-radius: 10px;
    padding: 10px;
    overflow-y: auto;

    .ai-chat-record {
      flex: 1;
      height: 100%;
      overflow-y: auto;

      .ai-chat-record-item {
        display: flex;
        flex-direction: column;
        justify-content: flex-start;
        align-items: flex-end;
        gap: 10px;
        margin-bottom: 10px;
        line-height: 1.5;

        .ai-chat-record-item-user {
          background-color: #f3f3f3;
          padding: 10px;
          border-radius: 10px;
        }

        .ai-chat-record-item-assistant {
          width: 100%;
          background-color: #fff;
          color: #333;
          padding: 10px;
          border-radius: 10px;
        }
      }
    }

    .ai-chat-btns {
      display: flex;
      flex-direction: row;
      justify-content: flex-start;
      align-items: center;
      gap: 10px;
      margin-bottom: 10px;
      border-top: 1px solid #e0e0e0;
      padding-top: 5px;

      .ai-chat-upload {
        ::v-deep {
          .el-form-item {
            margin-bottom: 0;
          }

          .el-upload-list {
            display: flex;
            flex-direction: row;
            justify-content: flex-start;
            align-items: center;
            gap: 10px;
          }
        }
      }
    }

    .ai-chat-input {
      display: flex;
      flex-direction: row;
      justify-content: space-between;
      align-items: center;
      gap: 10px;
      position: relative;

      .ai-chat-input-textarea {
        width: 100%;
        height: 100px;
        resize: none;
        border: 1px solid #e0e0e0;
        border-radius: 10px;
        padding: 10px;
      }

      .chat-abort-icon {
        position: absolute;
        bottom: 5px;
        right: 20px;
        cursor: pointer;
        color: #999;
        font-size: 30px;

        &:hover {
          color: #335ab7;
        }
      }
    }

    .ai-chat-conversation-id {
      font-size: 12px;
      color: #999;
      margin-top: 10px;
    }
  }
}
</style>

<style lang="scss">
code {
  white-space: pre-wrap;
  word-break: break-word;
  overflow-wrap: break-word;
  background-color: #f9f9f9;
  padding: 2px 3px;
  border-radius: 5px;
  border: 1px solid #e0e0e0;
  margin: 0 5px;
}
</style>
